#pragma once

#include <string>
#include <ostream>


namespace book
{

class MdToken
{
public:

    enum class Type
    {
        // 无效类型，表示错误情况
        Invalid,
        
        // 普通文本
        Text,

        // *xxx* 斜体
        StarItalic,

        // **xxx** 粗体
        StarBold,

        // ***xxx*** 粗斜体
        StarBoldItalic,

        // _xxx_ 斜体
        UnderlineItalic,

        // __xxx__ 粗体
        UnderlineBold,

        // ___xxx___ 粗斜体
        UnderlineBoldItalic,

        // ~~***~~ 删除线
        DeleteLine,

        // `***` 代码
        Code,

        // < 未命名超链接的左侧
        UnnamedHrefLeftSide,

        // > 未命名超链接的右侧
        UnnamedHrefRightSide,
    };

    static const char *str(Type type);

    static const char *raw(Type type);

    MdToken() {}

    MdToken(MdToken &&b) noexcept
        : type_(b.type_)
        , text_(std::move(b.text_)) {}

    MdToken(Type type)
        : type_(type) {}

    MdToken(Type type, std::string &&text)
        : type_(type)
        , text_(std::move(text)) {}

    MdToken &operator=(MdToken &&b) noexcept {
        if (this != &b) {
            type_ = b.type_;
            text_ = std::move(b.text_);
        }
        return *this;
    }

    operator bool() const {
        return type_ != Type::Invalid;
    }

    bool operator==(const MdToken &b) const {
        return type_ == b.type_ && text_ == b.text_;
    }

    Type type() const { return type_; }

    const std::string &text() const { return text_; }

private:
    Type type_ = Type::Invalid;
    std::string text_;

    friend std::ostream &operator<<(std::ostream &out, const MdToken &obj) {
        out << "(" << str(obj.type());
        if (obj.type() == Type::Text) {
            out << ":" << obj.text();
        }
        out << ")";
        return out;
    }
};

} // namespace book
